//===--- OverloadChoice.h - A Choice from an Overload Set  ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides the \c OverloadChoice class and its related types,
// which is used by the constraint-based type checker to describe the
// selection of a particular overload from a set.
//
//===----------------------------------------------------------------------===//
#ifndef POLARPHP_SEMA_INTERNAL_OVERLOADCHOICE_H
#define POLARPHP_SEMA_INTERNAL_OVERLOADCHOICE_H

#include "llvm/ADT/PointerIntPair.h"
#include "llvm/Support/ErrorHandling.h"
#include "polarphp/ast/Availability.h"
#include "polarphp/ast/FunctionRefKind.h"
#include "polarphp/ast/Types.h"

namespace polar {

class ValueDecl;

namespace constraints {
class ConstraintSystem;

/// The kind of overload choice.
enum class OverloadChoiceKind : int {
   /// The overload choice selects a particular declaration from a
   /// set of declarations.
      Decl,
   /// The overload choice selects a particular declaration that was
   /// found via dynamic lookup and, therefore, might not actually be
   /// available at runtime.
      DeclViaDynamic,
   /// The overload choice equates the member type with the
   /// base type. Used for unresolved member expressions like ".none" that
   /// refer to enum members with unit type.
      BaseType,
   /// The overload choice selects a key path subscripting operation.
      KeyPathApplication,
   /// The member is looked up using @dynamicMemberLookup.
      DynamicMemberLookup,
   /// The member with KeyPath parameter is looked up using
   /// @dynamicMemberLookup.
      KeyPathDynamicMemberLookup,
   /// The overload choice selects a particular declaration that
   /// was found by bridging the base value type to its Objective-C
   /// class type.
      DeclViaBridge,
   /// The overload choice selects a particular declaration that
   /// was found by unwrapping an optional context type.
      DeclViaUnwrappedOptional,
   /// The overload choice indexes into a tuple. Index zero will
   /// have the value of this enumerator, index one will have the value of this
   /// enumerator + 1, and so on. Thus, this enumerator must always be last.
      TupleIndex,
};

/// Describes a particular choice within an overload set.
///
class OverloadChoice {
   enum : unsigned {
      /// Indicates that this is a normal "Decl" kind, or isn't a decl.
         IsDecl = 0x00,
      /// Indicates that this declaration was bridged, turning a
      /// "Decl" kind into "DeclViaBridge" kind.
         IsDeclViaBridge = 0x01,
      /// Indicates that this declaration was resolved by unwrapping an
      /// optional context type, turning a "Decl" kind into
      /// "DeclViaUnwrappedOptional".
         IsDeclViaUnwrappedOptional = 0x02,
      /// Indicates that this declaration was dynamic, turning a
      /// "Decl" kind into "DeclViaDynamic" kind.
         IsDeclViaDynamic = 0x03,
   };

   /// The base type to be used when referencing the declaration
   /// along with the three bits above.
   llvm::PointerIntPair<Type, 3, unsigned> BaseAndDeclKind;

   /// We mash together OverloadChoiceKind with tuple indices into a single
   /// integer representation.
   using OverloadChoiceKindWithTupleIndex =
   llvm::PointerEmbeddedInt<uint32_t, 29>;

   /// Depending on the OverloadChoiceKind, this could be one of two cases:
   /// 1) A ValueDecl for the cases that match to a Decl.  The exactly kind of
   ///    decl reference is disambiguated with the DeclKind bits in
   ///    BaseAndDeclKind.
   /// 2) An OverloadChoiceKindWithTupleIndex if this is an overload kind without
   ///    a decl (e.g., a BaseType, keypath, tuple, etc).
   ///
   llvm::PointerUnion<ValueDecl*, OverloadChoiceKindWithTupleIndex> DeclOrKind;

   /// If this OverloadChoice represents a DynamicMemberLookup result,
   /// then this holds the identifier for the original member being
   /// looked up, as well as 1 bit tag which identifies whether this
   /// choice represents a key-path based dynamic lookup.
   llvm::PointerIntPair<Identifier, 1, unsigned> DynamicMember;

   /// This holds the kind of function reference (Unapplied, SingleApply,
   /// DoubleApply, Compound).
   /// FIXME: This needs two bits. Can we pack them somewhere?
   FunctionRefKind TheFunctionRefKind;

public:
   OverloadChoice()
      : BaseAndDeclKind(nullptr, 0), DeclOrKind(),
        TheFunctionRefKind(FunctionRefKind::Unapplied) {}

   OverloadChoice(Type base, ValueDecl *value,
                  FunctionRefKind functionRefKind)
      : BaseAndDeclKind(base, 0),
        TheFunctionRefKind(functionRefKind) {
      assert(!base || !base->hasTypeParameter());
      assert((reinterpret_cast<uintptr_t>(value) & (uintptr_t)0x03) == 0 &&
             "Badly aligned decl");

      DeclOrKind = value;
   }

   OverloadChoice(Type base, OverloadChoiceKind kind)
      : BaseAndDeclKind(base, 0), DeclOrKind(uint32_t(kind)),
        TheFunctionRefKind(FunctionRefKind::Unapplied) {
      assert(base && "Must have a base type for overload choice");
      assert(!base->hasTypeParameter());
      assert(kind != OverloadChoiceKind::Decl &&
             kind != OverloadChoiceKind::DeclViaDynamic &&
             kind != OverloadChoiceKind::DeclViaBridge &&
             kind != OverloadChoiceKind::DeclViaUnwrappedOptional &&
             "wrong constructor for decl");
   }

   OverloadChoice(Type base, unsigned index)
      : BaseAndDeclKind(base, 0),
        DeclOrKind(uint32_t(OverloadChoiceKind::TupleIndex)+index),
        TheFunctionRefKind(FunctionRefKind::Unapplied) {
      assert(base->getRValueType()->is<TupleType>() && "Must have tuple type");
   }

   bool isInvalid() const {
      return BaseAndDeclKind.getPointer().isNull() &&
             BaseAndDeclKind.getInt() == 0 &&
             DeclOrKind.isNull() &&
             TheFunctionRefKind == FunctionRefKind::Unapplied;
   }

   /// Retrieve an overload choice for a declaration that was found via
   /// dynamic lookup.
   static OverloadChoice getDeclViaDynamic(Type base, ValueDecl *value,
                                           FunctionRefKind functionRefKind) {
      OverloadChoice result;
      result.BaseAndDeclKind.setPointer(base);
      result.BaseAndDeclKind.setInt(IsDeclViaDynamic);
      result.DeclOrKind = value;
      result.TheFunctionRefKind = functionRefKind;
      return result;
   }

   /// Retrieve an overload choice for a declaration that was found via
   /// bridging to an Objective-C class.
   static OverloadChoice getDeclViaBridge(Type base, ValueDecl *value,
                                          FunctionRefKind functionRefKind) {
      OverloadChoice result;
      result.BaseAndDeclKind.setPointer(base);
      result.BaseAndDeclKind.setInt(IsDeclViaBridge);
      result.DeclOrKind = value;
      result.TheFunctionRefKind = functionRefKind;
      return result;
   }

   /// Retrieve an overload choice for a declaration that was found
   /// by unwrapping an optional context type.
   static OverloadChoice
   getDeclViaUnwrappedOptional(Type base, ValueDecl *value,
                               FunctionRefKind functionRefKind) {
      OverloadChoice result;
      result.BaseAndDeclKind.setPointer(base);
      result.BaseAndDeclKind.setInt(IsDeclViaUnwrappedOptional);
      result.DeclOrKind = value;
      result.TheFunctionRefKind = functionRefKind;
      return result;
   }

   /// Retrieve an overload choice for a declaration that was found via
   /// dynamic member lookup. The `ValueDecl` is a `subscript(dynamicMember:)`
   /// method.
   static OverloadChoice getDynamicMemberLookup(Type base, ValueDecl *value,
                                                Identifier name,
                                                bool isKeyPathBased) {
      OverloadChoice result;
      result.BaseAndDeclKind.setPointer(base);
      result.DeclOrKind = value;
      result.DynamicMember.setPointer(name);
      result.DynamicMember.setInt(isKeyPathBased);
      result.TheFunctionRefKind = FunctionRefKind::SingleApply;
      return result;
   }

   /// Retrieve the base type used to refer to the declaration.
   Type getBaseType() const {
      return BaseAndDeclKind.getPointer();
   }

   /// Determines the kind of overload choice this is.
   OverloadChoiceKind getKind() const {
      if (!DynamicMember.getPointer().empty()) {
         return DynamicMember.getInt()
                ? OverloadChoiceKind::KeyPathDynamicMemberLookup
                : OverloadChoiceKind::DynamicMemberLookup;
      }

      if (DeclOrKind.is<ValueDecl*>()) {
         switch (BaseAndDeclKind.getInt()) {
            case IsDeclViaBridge: return OverloadChoiceKind::DeclViaBridge;
            case IsDeclViaDynamic: return OverloadChoiceKind::DeclViaDynamic;
            case IsDeclViaUnwrappedOptional:
               return OverloadChoiceKind::DeclViaUnwrappedOptional;
            default: return OverloadChoiceKind::Decl;
         }
      }
      uint32_t kind = DeclOrKind.get<OverloadChoiceKindWithTupleIndex>();
      if (kind >= (uint32_t)OverloadChoiceKind::TupleIndex)
         return OverloadChoiceKind::TupleIndex;

      return (OverloadChoiceKind)kind;
   }

   /// Determine whether this choice is for a declaration.
   bool isDecl() const {
      return DeclOrKind.is<ValueDecl*>();
   }

   /// Retrieve the declaration that corresponds to this overload choice.
   ValueDecl *getDecl() const {
      return DeclOrKind.get<ValueDecl*>();
   }

   /// Retrieves the declaration that corresponds to this overload choice, or
   /// \c nullptr if this choice is not for a declaration.
   ValueDecl *getDeclOrNull() const {
      return isDecl() ? getDecl() : nullptr;
   }

   /// Returns true if this is either a decl for an optional that was
   /// declared as one that can be implicitly unwrapped, or is a
   /// function-typed decl that has a return value that is implicitly
   /// unwrapped.
   bool isImplicitlyUnwrappedValueOrReturnValue() const;

   bool isKeyPathDynamicMemberLookup() const {
      return getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup;
   }

   /// Get the name of the overload choice.
   DeclName getName() const;

   /// Retrieve the tuple index that corresponds to this overload
   /// choice.
   unsigned getTupleIndex() const {
      assert(getKind() == OverloadChoiceKind::TupleIndex);
      uint32_t kind = DeclOrKind.get<OverloadChoiceKindWithTupleIndex>();
      return kind-(uint32_t)OverloadChoiceKind::TupleIndex;
   }

   /// Retrieves an opaque choice that ignores the base type.
   void *getOpaqueChoiceSimple() const {
      return DeclOrKind.getOpaqueValue();
   }

   FunctionRefKind getFunctionRefKind() const {
      assert(isDecl() && "only makes sense for declaration choices");
      return TheFunctionRefKind;
   }
};

} // end namespace constraints
} // end namespace polar

#endif // POLARPHP_SEMA_INTERNAL_OVERLOADCHOICE_H
